<?php
/**
*	@author SignpostMarv Martin
*	@package UHU
*	@subpackage UHU_Components
**/

/**
*	Interface for uhu_collection- defines two functions for auto validation of input
*	@package UHU_Components
*	@subpackage Collections
*/
interface uhu_collection extends Iterator,Countable,ArrayAccess
{
/**
*	Intended to throw an InvalidArgumentException object if the value is not to be accepted for the instantiated collection.
*	@param mixed the item to add to the collection
*	@return void
*/
	public function validate_value($val);
/**
*	Intended to throw an InvalidArgumentException object if the offset is not to be accepted for the instantiated collection.
*	@param mixed the offset for the item to add to the collection
*	@return void
*/
	public function validate_offset($offset);
}

/**
*	Defines a basic general-purpose collection object
*	@package UHU_Components
*	@subpackage Collections
*	@uses uhu_collection
*/
abstract class uhu_collection_basic implements uhu_collection
{
/**
*	@var array An array to hold the collection items
*/
	protected $collection = array();
/**
*	An empty offset validator, leaves offset errors up to the PHP core
*	@uses uhu_collection::validate_offset()
*/
	public function validate_offset($offset)
	{}

/**
*	Creates the collection object, optionally adding items on instantiation
*	@param array|null Optional array of items to add when instantiating the collection
*	@uses uhu_collection_basic::offsetSet()
*/
	public function __construct(array $items=null)
	{
		if(empty($items) === false)
		{
			foreach($items as $k=>$v)
			{
				$this[$k] = $v;
			}
		}
	}
/**
*	@return integer The number of items in the collection
*	@uses uhu_collection_basic::$collection
*/
	public function count()
	{
		return count($this->collection);
	}
/**
*	@return mixed The curren item in the collection
*	@uses uhu_collection_basic::$collection
*/
	public function current()
	{
		return current($this->collection);
	}
/**
*	@return mixed The key for the curren item in the collection
*	@uses uhu_collection_basic::$collection
*/
	public function key()
	{
		return key($this->collection);
	}
/**
*	@return mixed Advances the array pointer for the collection
*	@uses uhu_collection_basic::$collection
*/
	public function next()
	{
		return next($this->collection);
	}
/**
*	@return mixed resets the array pointer for the collection
*	@uses uhu_collection_basic::$collection
*/
	public function rewind()
	{
		return reset($this->collection);
	}
/**
*	@return bool Returns TRUE if there is an item on the current array pointer
*	@uses uhu_collection_basic::$collection
*/
	public function valid()
	{
		return (bool)$this->current();
	}
/**
*	@return bool Returns TRUE if an item exists at the specified offset exists in the collection
*	@uses uhu_collection_basic::$collection
*/
	public function offsetExists($offset)
	{
		return isset($this->collection[$offset]);
	}
/**
*	@return void Removes the item at the specified offset in the collection
*	@uses uhu_collection_basic::$collection
*/
	public function offsetUnset($offset)
	{
		unset($this->collection[$offset]);
	}
/**
*	@return mixed Returns the item at the specified offset in the collection, or NULL if no such item exists
*	@uses uhu_collection_basic::$collection
*/
	public function offsetGet($offset)
	{
		return $this->offsetExists($offset) ? $this->collection[$offset] : null;
	}
/**
*	@return void Adds the item provided to the collection at the specified offset. If the offset is null (via $foo[] = 'bar'; ), then the offset is set to the number of entries in the collection
*	@uses uhu_collection_basic::$collection
*	@uses uhu_collection_basic::validate_offset()
*	@uses uhu_collection::validate_value()
*/
	public function offsetSet($offset,$value)
	{
		$offset = is_null($offset) ? count($this) : $offset;
		$this->validate_offset($offset);
		$this->validate_value($value);
		$this->collection[$offset] = $value;
	}
}

/**
*	Defines the interface for collections which are intended to store a specific class as items.
*	@package UHU_Components
*	@subpackage Collections
*/
interface uhu_collection_class_reflector
{
/**
*	@return ReflectionClass The ReflectionClass object for the class the collection is meant to store
*/
	public static function class_reflector();
}

/**
*	Provides automated validation of collection values that are intended to be an instance of a pre-determined class
*	@package UHU_Components
*	@subpackage Collections
*	@uses uhu_collection_class_reflector
*	@uses uhu_collection_basic
*/
abstract class uhu_collection_class extends uhu_collection_basic implements uhu_collection_class_reflector
{
/**
*	Determines if the given value is an instance of the class the collection is intended to store.
*	@return void
*	@uses uhu_collection_class_reflector::class_reflector()
*	@uses uhu_collection::validate_value()
*/
	public function validate_value($val)
	{
		if(is_object($val) === false)
		{
			throw new InvalidArgumentException('Value must be an instance of a class.');
		}
		if(call_user_func(get_class($this) . '::class_reflector')->isInstance($val) === false)
		{
			$foo = new ReflectionClass($val);
			if($foo->isSubclassOf(call_user_func(get_class($this) . '::class_reflector')) === false)
			{
				throw new InvalidArgumentException('Value (an instance of ' . get_class($val) . ') must be a sub-class of ' . call_user_func(get_class($this) . '::class_reflector')->getName());
			}
		}
	}
}

/**
*	Defines a collection class for housing other collection objects
*	@package UHU_Components
*	@subpackage Collections
*	@uses uhu_collection_class
*/
class uhu_collection_Collection extends uhu_collection_class
{
/**
*	@return ReflectionClass Returns an instance of ReflectionClass for the uhu_collection class
*	@uses uhu_collection
*/
	public static function class_reflector()
	{
		static $reflector;
		if(isset($reflector) === false)
		{
			$reflector = new ReflectionClass('uhu_collection');
		}
		return $reflector;
	}
}
?>